﻿using UnityEngine;
using UnityEditor;
using UnityEngine.TextCore;

using TMPro;
using zbitmapfont;
using UnityEngine.TextCore.LowLevel;
using System.Drawing;
using System.Runtime.InteropServices;
using zbitmapfont;

namespace zbitmapfont
{
    public static class BitmapFont2TMPro
    {
        static void PatchGlyph(RawCharacterInfo character, int textureHeight, int textureWidth, ref Glyph g)
        {
            var scaleH = textureWidth / textureHeight > 1 ? textureWidth / textureHeight : 1;
            var scaleW = textureHeight / textureWidth > 1 ? textureHeight / textureWidth : 1;
            g.glyphRect = new GlyphRect(
                character.X * scaleW,
                (textureHeight - character.Y - character.Height) * scaleH,
                character.Width * scaleW,
                character.Height * scaleH
            );
            g.metrics = new GlyphMetrics(
                character.Width,
                character.Height,
                character.Xoffset,
                -character.Yoffset,
                character.Xadvance
            );
        }

        [MenuItem("Assets/zbitmapfont/CreateTMP_FontAsset")]
        public static void CreateTMP_FontAsset()
        {
            foreach (var a in Selection.assetGUIDs)
            {
                var p = AssetDatabase.GUIDToAssetPath(a);
                
                if (System.IO.Directory.Exists(p))
                {
                    string destPath = p;
                    if (!BitmapFontTool.CreateBitmapFont(p, destPath))
                    {
                        throw new System.Exception("CreateBitmapFont failed: " + p);
                    }

                    p += ".fnt";
                }

                if (!p.EndsWith(".fnt"))
                {
                    continue;
                }

                if (!System.IO.File.Exists(p))
                {
                    continue;
                }

                Create(p);
            }
            
        }

        static void Create(string fntFilePath)
        {
            var pngPath = System.IO.Path.GetDirectoryName(fntFilePath) + "/" + System.IO.Path.GetFileNameWithoutExtension(fntFilePath) + "_0.png";


            Texture2D m_FontAtlasTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(pngPath);


            var assetPath = System.IO.Path.GetDirectoryName(fntFilePath) + "/" + System.IO.Path.GetFileNameWithoutExtension(fntFilePath) + ".asset";


            var tex_FileName = System.IO.Path.GetFileNameWithoutExtension(assetPath);

            //Debug.Log("Creating TextMeshPro font asset!");

            var fontAsset = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(assetPath);
            if (fontAsset == null)
            {
                fontAsset = ScriptableObject.CreateInstance<TMP_FontAsset>(); // Create new TextMeshPro Font Asset.
                AssetDatabase.CreateAsset(fontAsset, assetPath);
            }

            // Set version number of font asset
            //fontAsset.version = "1.1.0";

            //Set Font Asset Type
            //fontAsset.atlasRenderMode = GlyphRenderMode.RASTER;

            // Reference to the source font file GUID.
            //fontAsset.m_SourceFontFile_EditorRef = m_DestinationFontFile.sourceFontFile;
            //fontAsset.m_SourceFontFileGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_DestinationFontFile.sourceFontFile));

            // Add FaceInfo to Font Asset
            //fontAsset.faceInfo = m_FaceInfo;

            //// Add GlyphInfo[] to Font Asset
            //fontAsset.glyphTable = m_FontGlyphTable;

            //// Add CharacterTable[] to font asset.
            //fontAsset.characterTable = m_FontCharacterTable;

            //// Sort glyph and character tables.
            //fontAsset.SortAllTables();

            //// Get and Add Kerning Pairs to Font Asset
            //if (m_IncludeFontFeatures)
            //    fontAsset.fontFeatureTable = GetKerningTable();


            // Add Font Atlas as Sub-Asset
            fontAsset.atlasTextures = new Texture2D[] { m_FontAtlasTexture };


            //fontAsset.atlasWidth = m_FontAtlasTexture.width;
            //fontAsset.atlasHeight = m_FontAtlasTexture.height;
            //fontAsset.atlasPadding = m_Padding;

            //AssetDatabase.AddObjectToAsset(m_FontAtlasTexture, fontAsset);

            // Create new Material and Add it as Sub-Asset
            Shader default_Shader = Shader.Find("TextMeshPro/Bitmap Custom Atlas"); // m_shaderSelection;
            Material tmp_material = AssetDatabase.LoadAssetAtPath<Material>(assetPath);
            if (tmp_material == null)
            {
                tmp_material = new Material(default_Shader);
                tmp_material.name = tex_FileName + " Material";
                fontAsset.material = tmp_material;
                AssetDatabase.AddObjectToAsset(tmp_material, fontAsset);
            }
            tmp_material.SetTexture(ShaderUtilities.ID_MainTex, m_FontAtlasTexture);




            fontAsset.GetType().GetProperty("version").SetValue(fontAsset, "1.1.0");

            fontAsset.GetType().GetProperty("atlasRenderMode").SetValue(fontAsset, (int)GlyphRenderMode.RASTER);

            //fontAsset.GetType().GetProperty("faceInfo").SetValue(fontAsset, m_DestinationFontFile.faceInfo);

            fontAsset.GetType().GetProperty("atlasWidth").SetValue(fontAsset, m_FontAtlasTexture.width);
            fontAsset.GetType().GetProperty("atlasHeight").SetValue(fontAsset, m_FontAtlasTexture.height);
            fontAsset.GetType().GetProperty("atlasPadding").SetValue(fontAsset, 1);

            //fontAsset.GetType().GetField("m_SourceFontFile_EditorRef", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).SetValue(fontAsset, m_DestinationFontFile.sourceFontFile);

            //fontAsset.GetType().GetField("m_SourceFontFileGUID", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).SetValue(fontAsset, AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_DestinationFontFile.sourceFontFile)));


            var fontFile = fontAsset;
            var fnt = FntParse.GetFntParse(System.IO.File.ReadAllText(fntFilePath));
            fontFile.characterTable.Clear();
            //fontFile.characterLookupTable.Clear();
            fontFile.glyphTable.Clear();
            //fontFile.glyphLookupTable.Clear();
            for (uint j = 0; j < fnt.charInfos.Length; j++)
            {
                var c = fnt.charInfos[j];
                var a = new TMP_Character();
                a.unicode = (uint)c.index;
                a.glyphIndex = j;

                a.textAsset = fontFile;


                var glyph = new Glyph();
                PatchGlyph(fnt.rawCharInfos[j],
                    fnt.textureHeight,
                    fnt.textureWidth,
                    ref glyph);

                glyph.index = j;
                glyph.atlasIndex = 0;

                a.glyph = glyph;

                fontFile.characterTable.Add(a);
                //fontFile.characterLookupTable.Add(a.unicode, a);

                fontFile.glyphTable.Add(glyph);
                //fontFile.glyphLookupTable.Add(j, glyph);
            }


            fontAsset.GetType().GetMethod("SortAllTables", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).Invoke(fontAsset, null);


            var maxHeight = 0;
            for (int j = 0; j < fnt.charInfos.Length; j++)
            {
                maxHeight = Mathf.Max(maxHeight, Mathf.Abs(fnt.charInfos[j].minY - fnt.charInfos[j].maxY));
            }


            var newFaceInfo = fontFile.faceInfo;
            //newFaceInfo.baseline = fnt.lineBaseHeight;
            //newFaceInfo.lineHeight = fnt.lineHeight;
            //newFaceInfo.ascentLine = fnt.lineHeight;

            newFaceInfo.baseline = 0;
            newFaceInfo.lineHeight = maxHeight;
            newFaceInfo.ascentLine = 0;
            newFaceInfo.pointSize = fnt.fontSize;

            newFaceInfo.descentLine = -maxHeight;

            var fontType = typeof(TMP_FontAsset);
            var faceInfoProperty = fontType.GetProperty("faceInfo");
            faceInfoProperty.SetValue(fontFile, newFaceInfo);

            EditorUtility.SetDirty(fontFile);



            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
            AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
        }
    }
}